#include "sdk.h"
#include <wx/clipbrd.h>
#include "filefilters.h"
#include "backtracedlg.h"
#include "M_Code_colourmanager.h"
#include "debuggermanager.h"

namespace {
  const int idList = wxNewId();
  const int idSwitch = wxNewId();
  const int idSave = wxNewId();
  const int idJump = wxNewId();
  const int idCopyToClipboard = wxNewId();
  const int idSettingJumpDefault = wxNewId();
  const int idSettingSwitchDefault = wxNewId();
}

BEGIN_EVENT_TABLE( BacktraceDlg, wxPanel )
  EVT_LIST_ITEM_RIGHT_CLICK( idList, BacktraceDlg::OnListRightClick )
  EVT_LIST_ITEM_ACTIVATED( idList, BacktraceDlg::OnDoubleClick )
  EVT_MENU( idSwitch, BacktraceDlg::OnSwitchFrame )
  EVT_MENU( idSave, BacktraceDlg::OnSave )
  EVT_MENU( idJump, BacktraceDlg::OnJump )
  EVT_MENU( idCopyToClipboard, BacktraceDlg::OnCopyToClipboard )
  EVT_MENU( idSettingJumpDefault, BacktraceDlg::OnSettingJumpDefault )
  EVT_MENU( idSettingSwitchDefault, BacktraceDlg::OnSettingSwitchDefault )
  EVT_UPDATE_UI( idSwitch, BacktraceDlg::OnUpdateUI )
END_EVENT_TABLE()

BacktraceDlg::BacktraceDlg( wxWindow* parent ) :
  wxPanel( parent ) {
  m_list = new wxListCtrl( this, idList, wxDefaultPosition, wxDefaultSize,
                           wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES );
  wxBoxSizer* bs = new wxBoxSizer( wxVERTICAL );
  bs->Add( m_list, 1, wxEXPAND | wxALL );
  SetAutoLayout( true );
  SetSizer( bs );
  m_list->InsertColumn( 0, _T( "Nr" ), wxLIST_FORMAT_RIGHT );
  m_list->InsertColumn( 1, _T( "Address" ), wxLIST_FORMAT_LEFT );
  m_list->InsertColumn( 2, _T( "Function" ), wxLIST_FORMAT_LEFT );
  m_list->InsertColumn( 3, _T( "File" ), wxLIST_FORMAT_LEFT, 128 );
  m_list->InsertColumn( 4, _T( "Line" ), wxLIST_FORMAT_RIGHT, 64 );
  Manager::Get()->GetColourManager()->RegisterColour( _T( "Debugger" ), _T( "Backtrace active frame background" ),
      _T( "dbg_backtrace_active_background" ), *wxRED );
  Manager::Get()->GetColourManager()->RegisterColour( _T( "Debugger" ), _T( "Backtrace active frame foreground" ),
      _T( "dbg_backtrace_active_foreground" ), *wxWHITE );
}

void BacktraceDlg::Reload() {
  cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
  if( !plugin ) {
    return;
  }
  ColourManager &colours = *Manager::Get()->GetColourManager();
  const wxColour &activeBackground = colours.GetColour( _T( "dbg_backtrace_active_background" ) );
  const wxColour &activeForeground = colours.GetColour( _T( "dbg_backtrace_active_foreground" ) );
  m_list->Freeze();
  m_list->DeleteAllItems();
  int active_frame = plugin->GetActiveStackFrame();
  int active_frame_index = 0;
  for( int ii = 0; ii < plugin->GetStackFrameCount(); ++ii ) {
    cb::shared_ptr<const cbStackFrame> frame = plugin->GetStackFrame( ii );
    wxString addr = frame->GetAddressAsString();
    wxString num;
    num.Printf( _T( "%d" ), frame->GetNumber() );
    int idx = m_list->InsertItem( m_list->GetItemCount(), num );
    m_list->SetItem( idx, 1, addr );
    m_list->SetItem( idx, 2, frame->GetSymbol() );
    m_list->SetItem( idx, 3, frame->GetFilename() );
    m_list->SetItem( idx, 4, frame->GetLine() );
    if( active_frame == frame->GetNumber() ) {
      active_frame_index = ii;
      m_list->SetItemBackgroundColour( ii, activeBackground );
      m_list->SetItemTextColour( ii, activeForeground );
    }
  }
  if( active_frame_index < m_list->GetItemCount() ) {
    m_list->EnsureVisible( active_frame_index );
  }
  m_list->Thaw();
  m_list->SetColumnWidth( 0, 32 );
  for( int i = 1; i < 4; ++i ) {
    m_list->SetColumnWidth( i, wxLIST_AUTOSIZE );
  }
}

void BacktraceDlg::EnableWindow( bool enable ) {
  Enable( enable );
  m_list->Enable( enable );
}

void BacktraceDlg::OnListRightClick( M_Code_unused wxListEvent& event ) {
  wxMenu m;
  m.Append( idJump, _T( "跳转到 文件/行" ) );
  m.Append( idSwitch, _T( "Switch to this frame" ) );
  m.Append( idSave, _T( "保存到文件..." ) );
  m.Append( idCopyToClipboard, _T( "复制到剪贴板" ) );
  m.AppendRadioItem( idSettingJumpDefault, _T( "双击跳转" ) );
  m.AppendRadioItem( idSettingSwitchDefault, _T( "打开双击" ) );
  bool jump_on_double_click = M_Code_DebuggerCommonConfig::GetFlag( M_Code_DebuggerCommonConfig::JumpOnDoubleClick );
  m.Check( idSettingJumpDefault, jump_on_double_click );
  m.Check( idSettingSwitchDefault, !jump_on_double_click );
  m_list->PopupMenu( &m );
}

void BacktraceDlg::OnJump( M_Code_unused wxCommandEvent& event ) {
  if( m_list->GetSelectedItemCount() == 0 ) {
    return;
  }
  int index = m_list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  wxListItem info;
  info.m_itemId = index;
  info.m_mask = wxLIST_MASK_TEXT;
  info.m_col = 3;
  wxString file = m_list->GetItem( info ) ? info.m_text : _T( "" );
  info.m_col = 4;
  wxString line = m_list->GetItem( info ) ? info.m_text : _T( "" );
  if( !file.IsEmpty() && !line.IsEmpty() ) {
    long line_number;
    line.ToLong( &line_number, 10 );
    cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
    if( plugin ) {
      plugin->SyncEditor( file, line_number, false );
    }
  }
}

void BacktraceDlg::OnSwitchFrame( M_Code_unused wxCommandEvent& event ) {
  if( !IsSwitchFrameEnabled() ) {
    return;
  }
  if( m_list->GetSelectedItemCount() == 0 ) {
    return;
  }
  int index = m_list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  long realFrameNr;
  if( m_list->GetItemText( index ).ToLong( &realFrameNr ) ) {
    cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
    if( plugin ) {
      plugin->SwitchToFrame( realFrameNr );
    }
  } else {
    msging( _T( "不知道帧号！" ), _T( "错误" ), wxICON_ERROR );
  }
}

void BacktraceDlg::OnDoubleClick( M_Code_unused wxListEvent& event ) {
  bool jump = M_Code_DebuggerCommonConfig::GetFlag( M_Code_DebuggerCommonConfig::JumpOnDoubleClick );
  wxCommandEvent evt;
  if( jump || !IsSwitchFrameEnabled() ) {
    OnJump( evt );
  } else {
    OnSwitchFrame( evt );
  }
}

void BacktraceDlg::OnSave( M_Code_unused wxCommandEvent& event ) {
  wxFileDialog dlg( this, _T( "另存为" ), wxEmptyString, wxEmptyString,
                    FileFilters::GetFilterAll(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() != wxID_OK ) {
    return;
  }
  wxFFileOutputStream output( dlg.GetPath() );
  wxTextOutputStream text( output );
  for( int ii = 0; ii < m_list->GetItemCount(); ++ii ) {
    wxListItem info;
    info.m_itemId = ii;
    info.m_col = 1;
    info.m_mask = wxLIST_MASK_TEXT;
    wxString addr = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    info.m_col = 2;
    wxString func = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    info.m_col = 3;
    wxString file = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    info.m_col = 4;
    wxString line = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    text << _T( '#' ) << m_list->GetItemText( ii ) << _T( ' ' )
         << addr << _T( '\t' )
         << func << _T( ' ' )
         << _T( '(' ) << file << _T( ':' ) << line << _T( ')' )
         << _T( '\n' );
  }
  msging( _T( "保存文件" ), _T( "结果" ), wxICON_INFORMATION );
}

void BacktraceDlg::OnCopyToClipboard( M_Code_unused wxCommandEvent& event ) {
  wxString text;
  for( int ii = 0; ii < m_list->GetItemCount(); ++ii ) {
    wxListItem info;
    info.m_itemId = ii;
    info.m_col = 1;
    info.m_mask = wxLIST_MASK_TEXT;
    wxString addr = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    info.m_col = 2;
    wxString func = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    info.m_col = 3;
    wxString file = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    info.m_col = 4;
    wxString line = m_list->GetItem( info ) && !info.m_text.IsEmpty() ? info.m_text : _T( "??" );
    text << _T( '#' ) << m_list->GetItemText( ii ) << _T( ' ' )
         << addr << _T( '\t' )
         << func << _T( ' ' )
         << _T( '(' ) << file << _T( ':' ) << line << _T( ')' )
         << _T( '\n' );
  }
  wxTextDataObject *object = new wxTextDataObject( text );
  if( wxTheClipboard->Open() ) {
    wxTheClipboard->SetData( object );
    wxTheClipboard->Close();
  }
}

void BacktraceDlg::OnSettingJumpDefault( wxCommandEvent& event ) {
  bool checked = event.IsChecked();
  M_Code_DebuggerCommonConfig::SetFlag( M_Code_DebuggerCommonConfig::JumpOnDoubleClick, checked );
}

void BacktraceDlg::OnSettingSwitchDefault( wxCommandEvent& event ) {
  bool checked = event.IsChecked();
  M_Code_DebuggerCommonConfig::SetFlag( M_Code_DebuggerCommonConfig::JumpOnDoubleClick, !checked );
}

void BacktraceDlg::OnUpdateUI( wxUpdateUIEvent &event ) {
  event.Enable( IsSwitchFrameEnabled() );
}

bool BacktraceDlg::IsSwitchFrameEnabled() const {
  cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
  return plugin && plugin->IsRunning() && plugin->IsStopped();
}
